/**
 * @file msgcli.c
 * @author Marco Luisi
 *  Created on: 16 Dec 2010
 *
 */

#include "msg.h"
#include "msgcli.h"
#include "comsock.h"


#include <fnmatch.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#include <unistd.h>


#define NUL '\0'

char one_pattern[] = "%ONE[ ]*[ ]*";

char *name;
char term[] = "\0";
int msgsock_fd;
int wr;
int go = 1;
message_t msg;
message_t incoming;
struct sockaddr_un msgsock_sa;

msg_buffer in_buf;

pthread_t fs_tid;
pthread_t ts_tid;

/**
 *
 * @param argc
 * @param argv
 * @return
 */
int main(int argc, char **argv) {

	/*Controllo presenza del nome utente*/
	if (argc  > 2 || argc < 2) {
		printf("MSGCLI: Errore, fornire un nome utente!\n");
		fflush(NULL);
		exit(EXIT_FAILURE);
	}
	name = argv[1];


	msg.buffer = NULL;
	msg.length = 0;
	incoming.buffer = NULL;
	incoming.length = 0;

	if(init(name) == -1){
		exit(EXIT_FAILURE);
	}

	/*Si avviano i thread*/
	pthread_create(&ts_tid, NULL, to_server, (void*) NULL);
	pthread_create(&fs_tid, NULL, from_server, (void*) NULL);


	/*Attesa terminazione thread*/

	pthread_join(ts_tid,NULL);
	pthread_join(fs_tid,NULL);

	close(msgsock_fd);
	exit(EXIT_SUCCESS);
}


/**
 *
 * @param name
 * @return
 */
int init(char *name){
	/*Parsing dei parametri*/
	EC_M1(name_check(name));

	/*Preparazione ed invio richiesta di login*/
	if(newMessage(MSG_CONNECT, name, &msg,strlen(name)+1) == 0){
		printf("MSGCLI: Errore nel messaggio di login.\n");
		return -1;
	}
	/*Tentativi di connesione al server*/
	msgsock_fd = openConnection(MSGSOCK_PATH);
	switch (msgsock_fd){

	case SNAMETOOLONG: {
		printf("MSGCLI: Errore, path del socket oltre i limiti di sistema.\n");
		return -1;
	}

	case -1: {
		perror("MSGCLI: Errore nell'apertura del socket:");
		return -1;
	}

	default: {
		break;
	}

	}

	/*Invio della richiesta di login o*/
	wr = sendMessage(msgsock_fd, &msg);
	switch(wr){

	case SEOF:{
		printf("MSGCLI: Errore, MSGSERVER disconnesso.\n");
		return -1;
	}

	case -1:{
		perror("MSGCLI: Errore nella comunicazione su socket:");
		return -1;
	}

	default: {
		break;
	}

	}
	/*Controllo della risposta del server*/
	receiveMessage(msgsock_fd, &incoming);

	switch(incoming.type) {
		case 'E':{
			printf("Errore: utente già connesso al sistema!\n");

			exit(EXIT_FAILURE);
		}
		case 'N':{
			printf("Errore: nome utente errato!\n");
			exit(EXIT_FAILURE);
		}
	}
	return 0;
}



/**
 *
 * @param name
 */
int name_check(char *name) {

	int i;
	int len;
	len = strlen(name);
	if (len > USERNAMELEN) {
		printf("MSGCLI: Errore inserire un nome utente lungo al massimo 256 caratteri\n");
		exit(EXIT_FAILURE);
	}

	for (i = 1; i < len; i++) {
		if (!(isalnum(name[i]))) {
			printf("MSGCLI: Errore inserire un nome utente composto di soli caratteri alfanumerici\n");
			exit(EXIT_FAILURE);
		}
	}
	return 0;

}

void* from_server(void* c){
	int rcv;

	fflush(stdout);

	while(go){
		rcv = receiveMessage(msgsock_fd, &incoming);
		if (rcv == SEOF) { /*Se viene chiuso il server*/
			/*printf("Errore, il server ha chiuso la connessione, MSGCLI in chiusura..\n");*/
			pthread_cancel(ts_tid);
			go = 0;
			continue;
		}

		if (rcv == -1) {
			continue;
		}

		/*E' stato correttamente ricevuto un messaggio, si procede alla stampa*/
		switch(incoming.type){

		case(MSG_EXIT):{
			go = 0;
			pthread_cancel(ts_tid);
			break;
		}

		case (MSG_NO):{
			printf("Errore: Utente destinatario non presente nella lista utenti abilitati!\n");
			break;
			}

		case (MSG_ERROR):{
			fprintf(stdout,"%s\n",incoming.buffer);
			fflush(stdout);
			break;
			}
		case (MSG_LIST):{
			fprintf(stdout,"[LIST] %s\n",incoming.buffer);
			fflush(stdout);
			break;
		}

		/*messaggi con mittente*/
		case (MSG_BCAST):{
			fprintf(stdout,"[BCAST]");
			fflush(stdout);
		}
		case (MSG_TO_ONE):{
			fprintf(stdout,"%s\n",incoming.buffer);
			fflush(stdout);
			break;
 		}

		}
		/*Si libera il buffer in entrata dal server*/
		if(incoming.buffer != NULL){
			free(incoming.buffer);
			incoming.length = 0;
		}

	}
	pthread_exit(NULL);
}

/**
 *
 * @param c
 */
void* to_server(void* c) {

	int parse;
	int wr;

	msg.buffer = NULL;
	msg.length = 0;
	while (go) {
		/*Parse analizza l'input da utente e prepara l'eventuale messaggio per il server*/
		parse = parse_message(&msg);
		switch (parse) {

		case 0: {
			go = 0;
			pthread_cancel(fs_tid);
			pthread_exit(NULL);
			break;
		}

		case 1: {
			wr = sendMessage(msgsock_fd, &msg);
			break;
		}

		case -1: {
			/*printf("comando errato;stampa menu comandi\n");*/
			break;
		}

		}

		/*Svuotamento buffer*/
		if (msg.buffer != NULL){
			free(msg.buffer);
			msg.buffer = NULL;
			msg.length = 0;
		}

	}
	pthread_exit(NULL);
}

/**
 *
 * @param
 * @return
 */
int parse_message(message_t* m){

	unsigned int buffer_len;
	int message_l;
	char parse_buffer[MAX_CANON + 1];
	char *message = NULL;
	char *p = NULL;
	char *temp = NULL;

	/*Lettura e setup buffer*/
	if(fgets(parse_buffer,MAX_CANON,stdin) == NULL){/*Controllo EOF*/
		exit(EXIT_FAILURE);
	}
	buffer_len = strlen(parse_buffer);
	parse_buffer[buffer_len - 1] = '\0';
	if(buffer_len == 1){
		return -1;
	}

    /*Letto l'input se ne verifica la validità
     *una prima distinzione si fa tra i messaggi broadcast e gli altri */
    if(parse_buffer[0] != '%'){
    	/*Messaggio broadcast*/
        message_l = buffer_len;
        message = strndup(parse_buffer, buffer_len);
        if(message == NULL){
        	perror("Errore strndup");
        	exit(EXIT_FAILURE);
        }
    	newMessage(MSG_BCAST, message, m, buffer_len);
    	return 1;
    }
    /*List*/
    if(strcmp(parse_buffer,LIST) == 0){
    	newMessage(MSG_LIST ,NULL,m,0);
    	return 1;
    }
    /*Exit*/
    if(strcmp(parse_buffer,EXIT) == 0){
    	newMessage(MSG_EXIT ,NULL,m,0);
    	return 0;
    }
    /*ToONE*/
    if(fnmatch(one_pattern,parse_buffer,0) == 0){
    	p = parse_buffer + 5;/*punta al nome del destinatario*/
    	message_l = strlen(p)+1;/*lunghezza destinatario + spazio + messaggio + \0*/
    	/*si alloca un buffer contenente destinatario e messaggio*/
    	message = strndup(p,message_l);
    	temp = strchr(message,' ');
      	*temp = '\0';
    	newMessage(MSG_TO_ONE ,message,m,message_l);
    	return 1;
    }
    return -1;
}
